package com.steakliu.sun.scanner;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 功能说明： 类扫描器
 * <p>
 * Original @Author: steakliu-刘牌, 2022-12-10  18:22
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
public class ClassScanner {
  private static final String FILE = "file";
  private static final String JAR = "jar";
  private static final String SUFFIX = ".class";
  private static final String CHA = ".";

  private static void findClassByFile(String packageName, String packagePath, boolean recursive, List<String> classNameList) {
    File dir = new File(packagePath);
    if (!dir.exists() || !dir.isDirectory()) {
      return;
    }
    File[] files = dir.listFiles(new FileFilter() {
      @Override
      public boolean accept(File file) {
        return (recursive && file.isDirectory()) || (file.getName().endsWith(SUFFIX));
      }
    });
    assert files != null;
    for (File file : files) {
      if (file.isDirectory()) {
        findClassByFile(packageName + CHA + file.getName(), file.getAbsolutePath(), recursive, classNameList);
      } else {
        String className = file.getName().substring(0, file.getName().length() - 6);
        classNameList.add(packageName + CHA + className);
      }
    }
  }

  private static String findClassByJar(String packageName , List<String> classNameList , boolean recursive, String packageDirName, URL url) throws IOException {
    JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
    Enumeration<JarEntry> entries = jarFile.entries();
    while (entries.hasMoreElements()){
      JarEntry jarEntry = entries.nextElement();
      String name = jarEntry.getName();
      if (name.charAt(0) == '/'){
        name = name.substring(1);
      }
      if (name.startsWith(packageName)){
        int index = name.lastIndexOf('/');
        if (index != -1){
          packageName = name.substring(0, index).replace('/','.');
        }
        if (index != -1 || recursive){
          if (name.endsWith(SUFFIX) && !jarEntry.isDirectory()){
            String className = name.substring(packageName.length() + 1, name.length() - 6);
            classNameList.add(packageName + CHA + className);
          }
        }
      }
    }
    return  packageName;
  }

  public static List<String> getClassNameList(String packageName) throws IOException {
    List<String> classNameList = new ArrayList<>();
    boolean recursive = true;
    String packageDirName = packageName.replace(".", "/");
    Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
    while (dirs.hasMoreElements()){
      URL url = dirs.nextElement();
      String protocol = url.getProtocol();
      if (FILE.endsWith(protocol)){
        String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
        findClassByFile(packageName, filePath, recursive, classNameList);
      }else if (JAR.equals(protocol)){
        packageName = findClassByJar(packageName, classNameList, recursive, packageDirName, url);
      }
    }
    return classNameList;
  }
}
